package com.yupi.yuaicodemother.ai.rag;

import dev.langchain4j.data.document.*;
import dev.langchain4j.data.document.parser.apache.tika.ApacheTikaDocumentParser;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import org.w3c.dom.Text;

import java.io.IOException;
import java.util.List;

/**
 * 在软件开发过程中，我们常常面临开发效率低下的问题，其中一个痛点就是知识获取成本高。
 * 例如，项目文档、API手册等通常以PDF形式存在，开发人员需要手动查阅，效率极低。
 * EtlRunner 类正是为了解决这个痛点而设计，它基于 RAG（Retrieval Augmented Generation）架构，
 * 执行 ETL（抽取、转换、加载）流程，将 PDF 文件中的知识转化为可被大模型高效检索的向量数据。
 * 
 * 亮点在于：
 * 1. 自动化处理：从文件加载到向量存储全流程自动化，减少人工干预
 * 2. 精准分段：使用递归分段策略，确保语义完整的同时控制片段长度
 * 3. 高效检索：将文本转换为向量存储，利用向量相似度实现快速知识检索
 * 
 * 技术深度与难度：
 * 需要理解文档解析、文本分段算法、嵌入向量生成等多领域知识，
 * 同时要处理不同格式文件的解析问题和向量存储的性能优化。
 * 
 * 实用度与优势：
 * 大幅降低知识获取成本，开发人员可以借助大模型快速检索所需知识，
 * 提升开发效率，尤其在大型项目和复杂文档场景下优势明显。
 */
@Slf4j
@Component
@Profile("etl")
@RequiredArgsConstructor
public class EtlRunner implements CommandLineRunner {

    //spring资源解析器
    private final ResourceLoader resourceLoader;
    //向量数据库
    private final EmbeddingStore<TextSegment> store;
    //向量转化模型
    private final EmbeddingModel embeddingModel;

    @Override
    public void run(String... args) {

        try {
            //读取并加载文档
            Resource manual = resourceLoader.getResource("classpath:java-manual");
            if (manual.exists()) {
                throw new FileNotFoundException("请检查资源路径" + manual.getDescription());
            }
            //解析并拆分文档
            Document document = new ApacheTikaDocumentParser().parse(manual.getInputStream());
            List<TextSegment> segments = DocumentSplitters.recursive(1200, 350).split(document);
            if (segments.isEmpty()) {
                throw new DocumentParseException("文档解析失败，请检查文档格式" + manual.getDescription());
            }
            //资源转为向量
            List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
            if (embeddings.isEmpty()) {
                throw new EmbeddingGenerationException("向量生成失败，请检查模型配置" + manual.getDescription());
            }
            //存入向量数据库
            store.addAll(embeddings,segments);
        } catch (IOException e) {
            throw new EtlIOException("IO导致异常",e);
        } catch (Exception e) {
            throw new EtlUnexpectedException("ETL异常",e);
        }
    }
}












